import * as Notifications from "expo-notifications";
import Constants from "expo-constants";
import { Platform } from "react-native";
import * as Device from "expo-device";
import {
    checkExpoPushToken,
    upsertExpoPushToken,
} from "@/services/api/expo-push-token";
import { ResponseCode } from "@/types/api-common";
import {NotificationMessageProps} from "@/types/notification/common";
import NotificationUtils from "@/utils/notification-utils";

// 初始化通知处理器
Notifications.setNotificationHandler({
    handleNotification: async () => ({
        shouldShowAlert: true,
        shouldPlaySound: true,
        shouldSetBadge: true,
    }),
});

const recentNotifications = new Map();
// 通知去重的时间窗口（毫秒）
const NOTIFICATION_DEDUPE_WINDOW = 6000; // 1分钟
// 通知频道
const NOTIFICATION_CHANNEL_ID = "chatMessageNotificationChannel";

/**
 * 发布通知
 * @param title
 * @param body
 * @param data
 */
async function publishNotification({
                                            title,
                                            body,
                                            data,
                                        }: NotificationMessageProps) {
    // 从data中获取senderId作为去重标识
    const senderId = data?.senderId;
    // 通知声音
    // const [{ localUri }] = await Asset.loadAsync(require('@/asserts/audio/message-notice-sound.mp3'));

    if (senderId) {
        const now = Date.now();
        const lastNotification = recentNotifications.get(senderId);
        // 检查是否有相同发送者的最近通知
        if (lastNotification) {
            // 如果在去重时间窗口内有相同发送者的通知
            if (now - lastNotification.timestamp < NOTIFICATION_DEDUPE_WINDOW) {
                // 如果内容相同或者非常相似，则跳过此次通知
                if (lastNotification.body === body) {
                    console.log("跳过重复通知:", title, body);
                    return;
                }
            }
        }
        // 更新最近通知记录
        recentNotifications.set(senderId, {
            timestamp: now,
            body: body,
        });

        // 清理过期的通知记录
        for (const [id, notification] of recentNotifications.entries()) {
            if (now - notification.timestamp > NOTIFICATION_DEDUPE_WINDOW) {
                recentNotifications.delete(id);
            }
        }

        await Notifications.scheduleNotificationAsync({
            content: {
                title: title,
                body: body,
                data: data,
            },
            trigger: {
                type: Notifications.SchedulableTriggerInputTypes.TIME_INTERVAL,
                seconds: 1,
            },
        });
    }
}

/**使用递归方式重试获取推送令牌
 @param maxRetries 最大重试次数
 @param retryDelay 重试延迟时间(毫秒)
 @param currentRetry 当前重试次数
 @returns 推送令牌 */
async function getPushTokenWithRetry(
    maxRetries = 3,
    retryDelay = 1000,
    currentRetry = 0
): Promise<string | undefined> {
    try {
        const projectId =
            Constants?.expoConfig?.extra?.eas?.projectId ??
            Constants?.easConfig?.projectId;
        if (!projectId) {
            throw new Error("Project ID not found");
        }
        const token = (await Notifications.getExpoPushTokenAsync({ projectId }))
            .data;
        console.log(`获取token成功: ${token}`);
        return token;
    } catch (error) {
        console.error(
            `获取Expo push token失败 (尝试 ${currentRetry + 1}/${maxRetries}), error`
        );
        if (currentRetry < maxRetries - 1) {
            console.log(`${retryDelay / 1000}秒后重试获取expo token...`);
            await new Promise((resolve) => setTimeout(resolve, retryDelay));
            return getPushTokenWithRetry(maxRetries, retryDelay, currentRetry + 1);
        }
        console.error(`达到最大重试次数(${maxRetries})，获取token失败`);
        return null;
    }
}

async function registerForPushNotificationsAsync() {
    // 请求通知权限
    const b = await NotificationUtils.requestNotificationPermissions();
    if (b) {
        if (Platform.OS === "android") {
            await Notifications.setNotificationChannelAsync(NOTIFICATION_CHANNEL_ID, {
                name: "聊天消息通知",
                importance: Notifications.AndroidImportance.DEFAULT, //通知级别
                description: "用于接收聊天消息的通知",
                showBadge: true,
                vibrationPattern: [0, 250, 250, 250],
                lightColor: "#FF231F7C",
            });
        }
        // Learn more about projectId:
        // https://expo.nodejs.cn/push-notifications/push-notifications-setup/#configure-projectid
        // EAS projectId is used here.
        if (Device.isDevice) {
            // Learn more about projectId:
            // https://expo.nodejs.cn/push-notifications/push-notifications-setup/#configure-projectid
            // EAS projectId is used here.
            const resp = await checkExpoPushToken(Device.osInternalBuildId);
            if (resp.data.code === ResponseCode.SUCCESS) {
                const hasPushToken = resp.data.data;
                if (!hasPushToken) {
                    const token = await getPushTokenWithRetry();
                    if (token) {
                        const resp = await upsertExpoPushToken({
                            deviceId: Device.osInternalBuildId,
                            token,
                        });
                        if (resp.data.code === ResponseCode.SUCCESS) {
                            console.log("Expo Push Token registered successfully:", token);
                        } else {
                            console.error(
                                "Failed to register Expo Push Token:",
                                resp.data.message
                            );
                        }
                    } else {
                        console.error("Failed to obtain Expo Push Token after retries");
                    }
                } else {
                    console.log(
                        "Expo Push Token already exists for this device:",
                        Device.osInternalBuildId
                    );
                }
            }
        } else {
            console.error("Must use physical device for Push Notifications");
        }
    }
}

async function unregisterForPushNotifications() {
    // 清除所有本地通知
     await Notifications.cancelAllScheduledNotificationsAsync();
    // 清除最近通知记录
    recentNotifications.clear();
    // 停止推送服务
    await Notifications.unregisterForNotificationsAsync()
}

export default {
    publishNotification,
    registerForPushNotificationsAsync,
    unregisterForPushNotifications
};
